package com.ruoyi.tj.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.conditions.ISqlSegment;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.tj.BizError;
import com.ruoyi.tj.Const;
import com.ruoyi.tj.entity.BaseEntity;
import com.ruoyi.tj.entity.Cheliangtingfang;
import com.ruoyi.tj.service.BaseBizService;
import com.ruoyi.tj.utils.ObjectUtils;
import com.ruoyi.tj.utils.UUIDUtils;
import com.ruoyi.tj.vo.BizQueryParam;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static com.baomidou.mybatisplus.core.enums.SqlKeyword.*;

public class BaseBizServiceImpl<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> implements BaseBizService<T> {

    @Override
    public T saveRecord(T record) throws Throwable {
        SysUser user = ShiroUtils.getSysUser();

        LocalDateTime createdTime = LocalDateTime.now();
        // record.setJiequId(user.getJieQuId());

        if(record.getId() == null) {
            record.setId(UUIDUtils.generate());
            record.setUserId(user.getUserId());
            record.setCreator(user.getUserId());
            record.setCreateTime(createdTime);
            record.setUpdater(user.getUserId());
            record.setUpdateTime(createdTime);
            record.setDeptId(user.getDeptId());
            record.setDeptPath(user.getDeptPath());
            record.setDeleted(false);
            if(record.getReviewed() == null) {
                record.setReviewed(0);
            }
            this.getBaseMapper().insert(record);
        } else {
            // 读取旧记录
            T oldRecord = this.getRecordById(record.getId());
            if(oldRecord == null || oldRecord.getDeleted()) {
                throw new Exception(BizError.RECORD_NOT_FOUND);
            }
            record.setUserId(oldRecord.getUserId());
            record.setDeptId(oldRecord.getDeptId());
            record.setDeptPath(oldRecord.getDeptPath());
            // 已经审核过的记录，要被更新成未审核，不允许
            if((record.getReviewed() == null || record.getReviewed() == 0) && oldRecord.getReviewed() == 1) {
                throw new Exception(BizError.ILLEGAL_OPT);
            }
            record.setUpdateTime(createdTime);
            record.setUpdater(user.getUserId());
            this.getBaseMapper().updateById(record);
        }
        return record;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Throwable.class)
    public Collection<T> saveRecordBath(Collection<T> records) throws Throwable {
        SysUser user = ShiroUtils.getSysUser();
        LocalDateTime createdTime = LocalDateTime.now();
        List<T> toInsert = new ArrayList<>();
        List<T> toUpdate = new ArrayList<>();
        for (T record : records) {

            if(record.getId() == null) {
                record.setId(UUIDUtils.generate());
                record.setUserId(user.getUserId());
                record.setCreator(user.getUserId());
                record.setCreateTime(createdTime);
                record.setUpdater(user.getUserId());
                record.setUpdateTime(createdTime);
                record.setDeptId(user.getDeptId());
                record.setDeptPath(user.getDeptPath());
                record.setDeleted(false);
                if(record.getReviewed() == null) {
                    record.setReviewed(0);
                }
                toInsert.add(record);
            } else {
                // 读取旧记录
                T oldRecord = this.getRecordById(record.getId());
                if(oldRecord == null || oldRecord.getDeleted()) {
                    throw new Exception(BizError.RECORD_NOT_FOUND);
                }
                // 已经审核过的记录，要被更新成未审核，不允许
                /*if((record.getReviewed() == null || record.getReviewed() == 0) && oldRecord.getReviewed() == 1) {
                    throw new Exception(BizError.ILLEGAL_OPT);
                }*/
                record.setUserId(oldRecord.getUserId());
                record.setDeptId(oldRecord.getDeptId());
                record.setDeptPath(oldRecord.getDeptPath());
                record.setUpdateTime(createdTime);
                record.setUpdater(user.getUserId());
                toUpdate.add(record);
            }
        }
        if(toInsert.size() > 0) {
            this.saveBatch(toInsert);
        }
        if(toUpdate.size() > 0) {
            this.updateBatchById(toUpdate);
        }
        return records;
    }

    @Override
    public T getRecord(T record, BizQueryParam params) {
        LambdaQueryWrapper<T> queryWrapper = new LambdaQueryWrapper<>();
        ObjectUtils.setEmptyStringFieldsNull(record);
        queryWrapper.setEntity(record);
        if(params.getBeginTime() != null) {
            queryWrapper.gt(T::getCreateTime, params.getBeginTime());
        }
        if(params.getEndTime() != null) {
            queryWrapper.lt(T::getCreateTime, params.getEndTime());
        }
        SysUser user = ShiroUtils.getSysUser();
        if(params.getScope() == Const.SCOPE_ALL) {
            queryWrapper.likeRight(T::getDeptPath, user.getDeptPath());
        } else if (params.getScope() == Const.SCOPE_GROUP){
            queryWrapper.eq(T::getDeptPath, user.getDeptPath());
        } else if(params.getScope() == Const.SCOPE_SELF) {
            queryWrapper.eq(T::getUserId, user.getUserId());
        } else {
            queryWrapper.eq(T::getUserId, user.getUserId());
        }
        queryWrapper.eq(T::getDeleted, 0);
        String sortCol = StrUtil.toUnderlineCase(params.getSortCol());
        ISqlSegment[] sqlSegments = {ORDER_BY, () -> sortCol, params.getSortType().equalsIgnoreCase("asc") ? ASC : DESC};
        queryWrapper.getExpression().add(sqlSegments);
        return this.getBaseMapper().selectOne(queryWrapper, false);
    }

    @Override
    public T getRecordById(String id) {
        return this.getBaseMapper().selectById(id);
    }


    @Override
    public IPage<T> listPage(T record, BizQueryParam params) {
        LambdaQueryWrapper<T> queryWrapper = new LambdaQueryWrapper<>();
        ObjectUtils.setEmptyStringFieldsNull(record);
        queryWrapper.setEntity(record);
        if(params.getBeginTime() != null) {
            queryWrapper.gt(T::getCreateTime, params.getBeginTime());
        }
        if(params.getEndTime() != null) {
            queryWrapper.lt(T::getCreateTime, params.getEndTime());
        }
        SysUser user = ShiroUtils.getSysUser();
        if(params.getScope() == Const.SCOPE_ALL) {
            queryWrapper.likeRight(T::getDeptPath, user.getDeptPath());
        } else if (params.getScope() == Const.SCOPE_GROUP){
            queryWrapper.eq(T::getDeptPath, user.getDeptPath());
        } else if(params.getScope() == Const.SCOPE_SELF) {
            queryWrapper.eq(T::getUserId, user.getUserId());
        } else {
            queryWrapper.eq(T::getUserId, user.getUserId());
        }

        int page = params.getPage();
        int size = params.getSize();
        page = page > 0 ? page : 1;
        size = size > 0 ? size : Const.PAGE_LIMIT;
        queryWrapper.eq(T::getDeleted, 0);
        String sortCol = StrUtil.toUnderlineCase(params.getSortCol());
        ISqlSegment[] sqlSegments = {ORDER_BY, () -> sortCol, params.getSortType().equalsIgnoreCase("asc") ? ASC : DESC};
        queryWrapper.getExpression().add(sqlSegments);
        IPage<T> iPage = new Page<>(page, size);
        return this.getBaseMapper().selectPage(iPage, queryWrapper);
    }

    @Override
    @Transactional
    public void deleteRecordByIds(Collection<String> ids) {
        //LambdaQueryWrapper<T> queryWrapper = new LambdaQueryWrapper<>();
        //queryWrapper.in(T::getId, ids);
        List<T> list = this.getBaseMapper().selectBatchIds(ids);
        for (T one : list) {
            one.setDeleted(true);
        }
        this.saveOrUpdateBatch(list);
    }

    @Override
    public void deleteRecordById(String id) {
        LambdaQueryWrapper<T> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(T::getId, id);
        T record = this.getRecordById(id);
        record.setDeleted(true);
        this.getBaseMapper().update(record, queryWrapper);
    }

}
